﻿//Copyright (c) 2010 Christopher E. S. King (http://chriskingconsulting.com)
//
//Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files 
// (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, 
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do 
// so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Xml.Linq;
using System.Diagnostics.Contracts;
using System.IO;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels.Ipc;
using System.Runtime.Remoting.Channels;
using System.Threading.Tasks;
using System.Threading;
using System.Collections.Concurrent;

using Microsoft.Build.Tasks;
using Microsoft.Build.BuildEngine;
using Microsoft.Build.Framework;
using BuildTask = Microsoft.Build.Utilities.Task;

using King.Extensions;
using King.Reflection;
using King.PostBuild;
using System.Runtime.InteropServices;

namespace King.BuildTasks {

    public class PostBuild : BuildTask {

        private void ExecuteTask(BuildTask task) {
            task.BuildEngine = BuildEngine;
            task.HostObject = HostObject;
            task.Execute();
        }

        public override bool Execute() {
            Log.LogMessage("King.PostBuild: Scanning assembly {0}.", Assembly);

            // open channel
            var ipcChannel = new IpcChannel("localhost:9090");
            ChannelServices.RegisterChannel(ipcChannel, false);

            // register log
            RemotingServices.SetObjectUriForMarshal(Log, "log");
            RemotingServices.Marshal(Log);
            var logUrl = ipcChannel.GetUrlsForUri("log").Single();

            // create weaver
            var appDomainUnloaded = new ManualResetEvent(false);
            var weaver = new Weaver(appDomainUnloaded);

            // register weaver
            RemotingServices.SetObjectUriForMarshal(weaver, "weaver");
            RemotingServices.Marshal(weaver);
            var weaverUrl = ipcChannel.GetUrlsForUri("weaver").Single();

            // create lexYacc
            var lexYacc = new LexYacc();
            RemotingServices.SetObjectUriForMarshal(lexYacc, "lexYacc");
            RemotingServices.Marshal(lexYacc);
            var lexYaccUrl = ipcChannel.GetUrlsForUri("lexYacc").Single();

            // appDomain
            new Thread(delegate() {
                var appDomain = AppDomain.CreateDomain("King.PostBuild",
                    securityInfo: AppDomain.CurrentDomain.Evidence,
                    appBasePath: WorkingDirectory,
                    appRelativeSearchPath: null,
                    shadowCopyFiles: false,
                    adInit: PostBuildTask.Main,
                    adInitArgs: new[] { 
                        logUrl, 
                        weaverUrl,
                        lexYaccUrl,
                        Assembly
                    });

                var o = appDomain.CreateInstanceAndUnwrap(
                    typeof(PostBuildTask).Assembly.FullName, typeof(PostBuildTask).FullName);

                AppDomain.Unload(appDomain);
                appDomainUnloaded.Set();
            }).Start();

            var wait = WaitHandle.WaitAny(new[] { 
                weaver.IlDasmRequested,
                appDomainUnloaded
            });

            if (wait == 0) {
                Weaver.Current = weaver;
                RewriteRequested = true;
            }

            return true;
        }

        [Output]
        public bool RewriteRequested { get; set; }
        [Required]
        public string Assembly { get; set; }
        [Required]
        public string WorkingDirectory { get; set; }
    }
    public class RewriteIl : BuildTask {

        public override bool Execute() {
            SourceIlPath.CopyFileTo(TargetIlPath, WorkingDirectory);

            var weaver = Weaver.Current;

            weaver.IlDasmEnd(File.ReadAllText(SourceIlPath));
            var rewrite = weaver.RewriteEnd();
            File.WriteAllText(TargetIlPath, rewrite);

            Weaver.Current = null;

            return true;
        }

        [Required]
        public string SourceIlPath { get; set; }
        [Required]
        public string TargetIlPath { get; set; }
        [Required]
        public string WorkingDirectory { get; set; }
    }
}
